# -*- coding: utf-8 -*-
"""
Module Description:
Date: 2018/3/7
Author:Bai Jin Ping
"""
from datetime import datetime
from pyshare.const.enums.remote_service_switch import ServiceSwitch
from common import server_config
from pyshare.framework.firefly_dc.global_errcode import GlobalErrCode
from pyshare.framework.interceptor.base_interceptor import BaseInterceptor
from pyshare.framework.firefly_dc.action_service import ActionService
import pyshare.framework.runtime_logger as log
from pyshare.framework.router.remote_communicate_handler import get_suitable_sender, ClientSender
from pyshare.utils.print_utils import echo
from pyshare.const.lang.tips_info import TipsInfo
from common import service_config
from pyshare.utils.version_utils import get_main_version
from pyshare.const import special_actionid
from pyshare.remote_services.session_service import SessionService
from pyshare.remote_services.session_service import SessionCode

# 会话校验中不需要校验game-token的接口
PASS_ACTION_LIST = [special_actionid.ACTIONID_REGISTER, special_actionid.ACTIONID_PRELOGIN]


class SignStrategy(object):
    """防脱机挂尝试策略"""
    # 签名字段名
    SIGN_PARAMS_NAME = 'goodthings'

    # 策略1
    STRATEGY_1 = 1
    # 策略2
    STRATEGY_2 = 2

    def __init__(self, request):
        # 注册/登录接口使用uid
        if request.action_id == special_actionid.ACTIONID_REGISTER \
                or request.action_id == special_actionid.ACTIONID_LOGIN \
                or request.action_id == special_actionid.ACTIONID_PRELOGIN \
                or request.action_id == special_actionid.ACTIONID_PROFILE:
            key = request.get_attribute('uid')
            strategy = SignStrategy.STRATEGY_1
        # 其他接口使用rid
        else:
            key = request.get_attribute('rid')
            strategy = SignStrategy.STRATEGY_2
        sign = request.get_attribute(SignStrategy.SIGN_PARAMS_NAME)

        if sign is None:
            # 到此处绝对是脱机挂,正常客户端都要传SIGN_PARAMS_NAME
            check_robot_msg = 'Robot :args={args}:'.format(args=request.args)
            log.warning(check_robot_msg)
            echo(check_robot_msg)

        # 依赖的唯一串: UserId/RoleGuid
        self.key = key
        # 签名策略
        self.strategy = strategy
        # 客户端传入的签名(int)
        self.client_sign = sign
        # action_id
        self.action_id = request.action_id

    @staticmethod
    def _sign_strategy_1(action_id, key, client_sign_num):
        if client_sign_num % 10 != SignStrategy.STRATEGY_1:
            return False

        A = sum([ord(c) for c in key[:5]])
        C = action_id
        return client_sign_num == (abs(A * A + 20170822 - C * (C - 2018)) * 10 + SignStrategy.STRATEGY_1)

    @staticmethod
    def _sign_strategy_2(action_id, key, client_sign_num):
        if client_sign_num % 10 != SignStrategy.STRATEGY_2:
            return False

        now = datetime.now()

        # 23:55~00:05,不进行校验
        if (now.hour == 23 and now.minute >= 55) or (now.hour == 0 and now.minute <= 5):
            return True

        A = sum([ord(c) for c in key[:5]])
        B = now.year * 10000 + now.month * 100 + now.day
        C = action_id
        return client_sign_num == (abs(A + B - 20170822 - C * (C + 8012)) * 10 + SignStrategy.STRATEGY_2)

    def verify(self):
        """
        校验签名
        :return: bool
        """
        # 无key的接口直接通过校验(缺少key不会处理该请求)
        if not self.key:
            return True

        if not self.client_sign:
            return False

        if isinstance(self.client_sign, basestring):
            if not self.client_sign.isdigit():
                return False
            else:
                self.client_sign = int(self.client_sign)

        if self.strategy == SignStrategy.STRATEGY_1:
            return SignStrategy._sign_strategy_1(self.action_id, self.key, self.client_sign)
        elif self.strategy == SignStrategy.STRATEGY_2:
            return SignStrategy._sign_strategy_2(self.action_id, self.key, self.client_sign)
        else:
            # 未知算法
            return False


class SenderInterceptor(BaseInterceptor):
    """请求合法性校验"""

    @staticmethod
    def pre_process(request, response):
        echo("request = ", request.args)

        remote_sender = get_suitable_sender(action_id=request.action_id)
        ver = request.get_attribute('ver', default=1.0)
        req_ver = get_main_version(ver=ver)

        # 客户端版本兼容判断 V3.1后增加对会话校验
        if service_config.VersionControl.check_client_ver() and remote_sender is ClientSender:
            if req_ver < service_config.VersionControl.min_compatible_client_ver():
                response.set_info(info=TipsInfo.GlobalKeys.versionTooOld, stat=GlobalErrCode.FAILED)
                return False

            """
            对会话做校验
            """

            # 字符串转小写处理
            gateway_version = request.args.get('gatewayversion').lower()
            user_center_version = request.args.get('usercenterversion').lower()

            # 网关v2+用户中心v4 且为预登陆、登录以外的接口才校验game-token
            if gateway_version.startswith('v2') and user_center_version.startswith('v4') \
                    and request.args.get('actionid') not in PASS_ACTION_LIST:

                game_token = request.args.get('gametoken')

                # 判断是否需要请求会话验证，并序列化结果
                session_code, res_session_token, res_game_token, session_result = SessionService.serializer_session_token(
                    game_token
                )

                # 头部参数增加session-token、game-token
                response.set_headers("Session-Token", res_session_token)
                response.set_headers("Game-Token", res_game_token)

                # 防止报错参数被覆盖，在self.session中暂存会话结果
                response.set_session("SessionCode", session_code)
                response.set_session("SessionResult", session_result)

                # 会话校验失败直接返回
                if session_code != SessionCode.success:

                    response.set_attribute("SessionCode", session_code)
                    response.set_attribute("SessionResult", session_result)

                    response.set_info(info=TipsInfo.GlobalKeys.defaultErrorInfo, stat=GlobalErrCode.FAILED)

                    return False

        # v2.2开启防脱机挂检测[基于真实客户端算法检测]
        if req_ver >= 2.2 \
                and service_config.get_remote_service_switch(ServiceSwitch.ClientSignCheck) \
                and remote_sender is ClientSender:

            sign_strategy_obj = SignStrategy(request)
            if not sign_strategy_obj.verify():
                response.set_info(info=TipsInfo.GlobalKeys.defaultErrorInfo, stat=GlobalErrCode.FAILED)
                return False

        # 是否已注册的请求
        if remote_sender is None or ActionService.get_target(request.action_id) is None:
            response.set_info(info=TipsInfo.GlobalKeys.illegalActionZero, stat=GlobalErrCode.ILLEGAL_ACTION)
            return False

        # 预验证
        if not remote_sender.pre_verify(request):
            response.set_info(info=TipsInfo.GlobalKeys.illegalActionOne, stat=GlobalErrCode.ILLEGAL_ACTION)

            log.warning('illegal action from ip: %s to action id: %d'
                        % (request.get_attribute('ip', 'unknown'), request.action_id))
            return False

        return True

    @staticmethod
    def post_process(request, response):
        pass



